home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / STUTTGART / LANG / ADA / GNAT / !gcc / adainc / 4 / adb / s-fileio < prev    next >
Text File  |  1996-02-12  |  28KB  |  937 lines

  1. ------------------------------------------------------------------------------
  2. --                                                                          --
  3. --                         GNAT RUN-TIME COMPONENTS                         --
  4. --                                                                          --
  5. --                       S Y S T E M . F I L E _ I O                        --
  6. --                                                                          --
  7. --                                 B o d y                                  --
  8. --                                                                          --
  9. --                            $Revision: 1.30 $                             --
  10. --                                                                          --
  11. --   Copyright (C) 1992,1993,1994,1995,1996 Free Software Foundation, Inc.  --
  12. --                                                                          --
  13. -- GNAT is free software;  you can  redistribute it  and/or modify it under --
  14. -- terms of the  GNU General Public License as published  by the Free Soft- --
  15. -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
  16. -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
  17. -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  18. -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
  19. -- for  more details.  You should have  received  a copy of the GNU General --
  20. -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
  21. -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
  22. -- MA 02111-1307, USA.                                                      --
  23. --                                                                          --
  24. -- As a special exception,  if other files  instantiate  generics from this --
  25. -- unit, or you link  this unit with other files  to produce an executable, --
  26. -- this  unit  does not  by itself cause  the resulting  executable  to  be --
  27. -- covered  by the  GNU  General  Public  License.  This exception does not --
  28. -- however invalidate  any other reasons why  the executable file  might be --
  29. -- covered by the  GNU Public License.                                      --
  30. --                                                                          --
  31. -- GNAT was originally developed  by the GNAT team at  New York University. --
  32. -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
  33. --                                                                          --
  34. ------------------------------------------------------------------------------
  35.  
  36. with Ada.Finalization;            use Ada.Finalization;
  37. with Ada.IO_Exceptions;           use Ada.IO_Exceptions;
  38. with Ada.Streams;                 use Ada.Streams;
  39. with Interfaces.C_Streams;        use Interfaces.C_Streams;
  40. with System.Tasking_Soft_Links;   use System.Tasking_Soft_Links;
  41. with Unchecked_Deallocation;
  42.  
  43. package body System.File_IO is
  44.  
  45.    use System.File_Control_Block;
  46.  
  47.    ----------------------
  48.    -- Global Variables --
  49.    ----------------------
  50.  
  51.    Open_Files : AFCB_Ptr;
  52.    --  This points to a list of AFCB's for all open files. This is a doubly
  53.    --  linked list, with the Prev pointer of the first entry, and the Next
  54.    --  pointer of the last entry containing null.
  55.  
  56.    type Temp_File_Record;
  57.    type Temp_File_Record_Ptr is access all Temp_File_Record;
  58.  
  59.    Temp_Base : constant String := "ADA_TEMP_";
  60.  
  61.    Temp_Len : constant := Temp_Base'Length + 6;
  62.    --  Length of temporary file name (6 = length of suffix added by mktemp)
  63.    --  This does not include the terminating NUL character.
  64.  
  65.    type Temp_File_Record is record
  66.       Name : String (1 .. Temp_Len + 1);
  67.       Next : Temp_File_Record_Ptr;
  68.    end record;
  69.    --  One of these is allocated for each temporary file created
  70.  
  71.    Temp_Files : Temp_File_Record_Ptr;
  72.    --  Points to list of names of temporary files
  73.  
  74.    type File_IO_Clean_Up_Type is new Controlled with null record;
  75.    --  The closing of all open files and deletion of temporary files is an
  76.    --  action which takes place at the end of execution of the main program.
  77.    --  This action can be implemented using a library level object which
  78.    --  gets finalized at the end of the main program execution. The above is
  79.    --  a controlled type introduced for this purpose.
  80.  
  81.    procedure Finalize (V : in out File_IO_Clean_Up_Type);
  82.    --  This is the finalize operation that is used to do the cleanup.
  83.  
  84.    File_IO_Clean_Up_Object : File_IO_Clean_Up_Type;
  85.    --  This is the single object of the type that triggers the finalization
  86.    --  call. Since it is at the library level, this happens just before the
  87.    --  environment task is finalized.
  88.  
  89.    -----------------------
  90.    -- Local Subprograms --
  91.    -----------------------
  92.  
  93.    procedure Free_String is new Unchecked_Deallocation (String, Pstring);
  94.  
  95.    subtype Fopen_String is String (1 .. 4);
  96.    --  Holds open string (longest is "w+b" & nul)
  97.  
  98.    procedure Fopen_Mode
  99.      (Mode    : File_Mode;
  100.       Text    : Boolean;
  101.       Creat   : Boolean;
  102.       Amethod : Character;
  103.       Fopstr  : out Fopen_String);
  104.    --  Determines proper open mode for a file to be opened in the given
  105.    --  Ada mode. Text is true for a text file and false otherwise, and
  106.    --  Creat is true for a create call, and False for an open call. The
  107.    --  value stored in Fopstr is a nul-terminated string suitable for a
  108.    --  call to fopen or freopen. Amethod is the character designating
  109.    --  the access method from the Access_Method field of the FCB.
  110.  
  111.    ---------------------
  112.    -- Check_File_Open --
  113.    ---------------------
  114.  
  115.    procedure Check_File_Open (File : AFCB_Ptr) is
  116.    begin
  117.       if File = null then
  118.          raise Status_Error;
  119.       end if;
  120.    end Check_File_Open;
  121.  
  122.    ----------------
  123.    -- Append_Set --
  124.    ----------------
  125.  
  126.    procedure Append_Set (File : AFCB_Ptr) is
  127.    begin
  128.       if File.Mode = Append_File then
  129.          if fseek (File.Stream, 0, SEEK_END) /= 0 then
  130.             raise Device_Error;
  131.          end if;
  132.       end if;
  133.    end Append_Set;
  134.  
  135.    ----------------
  136.    -- Chain_File --
  137.    ----------------
  138.  
  139.    procedure Chain_File (File : AFCB_Ptr) is
  140.    begin
  141.       File.Next := Open_Files;
  142.       File.Prev := null;
  143.       Open_Files := File;
  144.  
  145.       if File.Next /= null then
  146.          File.Next.Prev := File;
  147.       end if;
  148.    end Chain_File;
  149.  
  150.    -----------------------
  151.    -- Check_Read_Status --
  152.    -----------------------
  153.  
  154.    procedure Check_Read_Status (File : AFCB_Ptr) is
  155.    begin
  156.       if File = null then
  157.          raise Status_Error;
  158.       elsif File.Mode > Inout_File then
  159.          raise Mode_Error;
  160.       end if;
  161.    end Check_Read_Status;
  162.  
  163.    ------------------------
  164.    -- Check_Write_Status --
  165.    ------------------------
  166.  
  167.    procedure Check_Write_Status (File : AFCB_Ptr) is
  168.    begin
  169.       if File = null then
  170.          raise Status_Error;
  171.       elsif File.Mode = In_File then
  172.          raise Mode_Error;
  173.       end if;
  174.    end Check_Write_Status;
  175.  
  176.    -----------
  177.    -- Close --
  178.    -----------
  179.  
  180.    procedure Close (File : in out AFCB_Ptr) is
  181.       Close_Status : int := 0;
  182.  
  183.    begin
  184.       Check_File_Open (File);
  185.       AFCB_Close (File);
  186.  
  187.       --  Sever the association between the given file and its associated
  188.       --  external file. The given file is left closed. Do not perform system
  189.       --  closes on the standard input, output and error files and also do
  190.       --  not attempt to close a stream that does not exist (signalled by a
  191.       --  null stream value -- happens in some error situations).
  192.  
  193.       if not File.Is_System_File
  194.         and then File.Stream /= NULL_Stream
  195.       then
  196.          Close_Status := fclose (File.Stream);
  197.       end if;
  198.  
  199.       --  Dechain file from list of open files and then free the storage
  200.       --  Since this is a global data structure, we have to protect against
  201.       --  multiple tasks attempting to access this list.
  202.  
  203.       Lock_Task;
  204.  
  205.       if File.Prev = null then
  206.          Open_Files := File.Next;
  207.       else
  208.          File.Prev.Next := File.Next;
  209.       end if;
  210.  
  211.       if File.Next /= null then
  212.          File.Next.Prev := File.Prev;
  213.       end if;
  214.  
  215.       Unlock_Task;
  216.  
  217.       --  Deallocate some parts of the file structure that were kept in heap
  218.       --  storage with the exception of system files (standard input, output
  219.       --  and error) since they had some information allocated in the stack.
  220.  
  221.       if not File.Is_System_File then
  222.          Free_String (File.Name);
  223.          Free_String (File.Form);
  224.          AFCB_Free (File);
  225.       end if;
  226.  
  227.       File := null;
  228.  
  229.       if Close_Status /= 0 then
  230.          raise Device_Error;
  231.       end if;
  232.    end Close;
  233.  
  234.    ------------
  235.    -- Delete --
  236.    ------------
  237.  
  238.    procedure Delete (File : in out AFCB_Ptr) is
  239.    begin
  240.       Check_File_Open (File);
  241.  
  242.       if not File.Is_Regular_File then
  243.          raise Use_Error;
  244.       end if;
  245.  
  246.       declare
  247.          Filename : aliased constant String := File.Name.all;
  248.  
  249.       begin
  250.          Close (File);
  251.  
  252.          if unlink (Filename'Address) = -1 then
  253.             raise Use_Error;
  254.          end if;
  255.       end;
  256.    end Delete;
  257.  
  258.    -----------------
  259.    -- End_Of_File --
  260.    -----------------
  261.  
  262.    function End_Of_File (File : AFCB_Ptr) return Boolean is
  263.    begin
  264.       Check_File_Open (File);
  265.  
  266.       if feof (File.Stream) /= 0 then
  267.          return True;
  268.  
  269.       else
  270.          Check_Read_Status (File);
  271.  
  272.          if ungetc (fgetc (File.Stream), File.Stream) = EOF then
  273.             clearerr (File.Stream);
  274.             return True;
  275.          else
  276.             return False;
  277.          end if;
  278.       end if;
  279.    end End_Of_File;
  280.  
  281.    --------------
  282.    -- Finalize --
  283.    --------------
  284.  
  285.    --  Note: we do not need to worry about locking against multiple task
  286.    --  access in this routine, since it is called only from the environment
  287.    --  task just before terminating execution.
  288.  
  289.    procedure Finalize (V : in out File_IO_Clean_Up_Type) is
  290.       Discard : int;
  291.       Fptr1   : AFCB_Ptr;
  292.       Fptr2   : AFCB_Ptr;
  293.    begin
  294.       --  First close all open files (the slightly complex form of this loop
  295.       --  is required because Close as a side effect nulls out its argument)
  296.  
  297.       Fptr1 := Open_Files;
  298.       while Fptr1 /= null loop
  299.          Fptr2 := Fptr1.Next;
  300.          Close (Fptr1);
  301.          Fptr1 := Fptr2;
  302.       end loop;
  303.  
  304.       --  Now unlink all temporary files. We do not bother to free the
  305.       --  blocks because we are just about to terminate the program. We
  306.       --  also ignore any errors while attempting these unlink operations.
  307.  
  308.       while Temp_Files /= null loop
  309.          Discard := unlink (Temp_Files.Name'Address);
  310.          Temp_Files := Temp_Files.Next;
  311.       end loop;
  312.  
  313.    end Finalize;
  314.  
  315.    -----------
  316.    -- Flush --
  317.    -----------
  318.  
  319.    procedure Flush (File : AFCB_Ptr) is
  320.    begin
  321.       Check_Write_Status (File);
  322.  
  323.       if fflush (File.Stream) = 0 then
  324.          return;
  325.       else
  326.          raise Device_Error;
  327.       end if;
  328.    end Flush;
  329.  
  330.    ----------------
  331.    -- Fopen_Mode --
  332.    ----------------
  333.  
  334.    --  The fopen mode to be used is shown by the following table:
  335.  
  336.    --                                     OPEN         CREATE
  337.    --     Append_File                     "r+"           "w+"
  338.    --     In_File                         "r"            "w+"
  339.    --     Out_File (Direct_IO)            "r+"           "w"
  340.    --     Out_File (all others)           "w"            "w"
  341.    --     Inout_File                      "r+"           "w+"
  342.  
  343.    --  Note: we do not use "a" or "a+" for Append_File, since this would not
  344.    --  work in the case of stream files, where even if in append file mode,
  345.    --  you can reset to earlier points in the file. The caller must use the
  346.    --  Append_Set routine to deal with the necessary positioning.
  347.  
  348.    --  Note: in several cases, the fopen mode used allows reading and
  349.    --  writing, but the setting of the Ada mode is more restrictive. For
  350.    --  instance, Create in In_File mode uses "r+" which allows writing,
  351.    --  but the Ada mode In_File will cause any write operations to be
  352.    --  rejected with Mode_Error in any case.
  353.  
  354.    --  Note: for the Out_File/Open cases for other than the Direct_IO case,
  355.    --  an initial call will be made by the caller to first open the file in
  356.    --  "r" mode to be sure that it exists. The real open, in "w" mode, will
  357.    --  then destroy this file. This is peculiar, but that's what Ada semantics
  358.    --  require and the ACVT tests insist on!
  359.  
  360.    --  If text file translation is required, then either b or t is
  361.    --  added to the mode, depending on the setting of Text.
  362.  
  363.    procedure Fopen_Mode
  364.      (Mode    : File_Mode;
  365.       Text    : Boolean;
  366.       Creat   : Boolean;
  367.       Amethod : Character;
  368.       Fopstr  : out Fopen_String)
  369.    is
  370.       Fptr  : Positive;
  371.  
  372.       text_translation_required : Boolean;
  373.       pragma Import (C, text_translation_required);
  374.  
  375.    begin
  376.       case Mode is
  377.          when In_File =>
  378.             if Creat then
  379.                Fopstr (1) := 'w';
  380.                Fopstr (2) := '+';
  381.                Fptr := 3;
  382.             else
  383.                Fopstr (1) := 'r';
  384.                Fptr := 2;
  385.             end if;
  386.  
  387.          when Out_File =>
  388.             if Amethod = 'D' and not Creat then
  389.                Fopstr (1) := 'r';
  390.                Fopstr (2) := '+';
  391.                Fptr := 3;
  392.             else
  393.                Fopstr (1) := 'w';
  394.                Fptr := 2;
  395.             end if;
  396.  
  397.          when Inout_File | Append_File =>
  398.             if Creat then
  399.                Fopstr (1) := 'w';
  400.             else
  401.                Fopstr (1) := 'r';
  402.             end if;
  403.  
  404.             Fopstr (2) := '+';
  405.             Fptr := 3;
  406.  
  407.       end case;
  408.  
  409.       --  If text_translation_required is true then we need to append
  410.       --  either a t or b to the string to get the right mode
  411.  
  412.       if text_translation_required then
  413.          if Text then
  414.             Fopstr (Fptr) := 't';
  415.          else
  416.             Fopstr (Fptr) := 'b';
  417.          end if;
  418.  
  419.          Fptr := Fptr + 1;
  420.       end if;
  421.  
  422.       Fopstr (Fptr) := Ascii.NUL;
  423.    end Fopen_Mode;
  424.  
  425.    ----------
  426.    -- Form --
  427.    ----------
  428.  
  429.    function Form (File : in AFCB_Ptr) return String is
  430.    begin
  431.       if File = null then
  432.          raise Status_Error;
  433.       else
  434.          return File.Form.all (1 .. File.Form'Length - 1);
  435.       end if;
  436.    end Form;
  437.  
  438.    ------------------
  439.    -- Form_Boolean --
  440.    ------------------
  441.  
  442.    function Form_Boolean
  443.      (Form    : String;
  444.       Keyword : String;
  445.       Default : Boolean)
  446.       return    Boolean
  447.    is
  448.       V1, V2 : Natural;
  449.  
  450.    begin
  451.       Form_Parameter (Form, Keyword, V1, V2);
  452.  
  453.       if V1 = 0 then
  454.          return Default;
  455.  
  456.       elsif Form (V1) = 'y' then
  457.          return True;
  458.  
  459.       elsif Form (V1) = 'n' then
  460.          return False;
  461.  
  462.       else
  463.          raise Use_Error;
  464.       end if;
  465.    end Form_Boolean;
  466.  
  467.    ------------------
  468.    -- Form_Integer --
  469.    ------------------
  470.  
  471.    function Form_Integer
  472.      (Form    : String;
  473.       Keyword : String;
  474.       Default : Integer)
  475.       return    Integer
  476.    is
  477.       V1, V2 : Natural;
  478.       V      : Integer;
  479.  
  480.    begin
  481.       Form_Parameter (Form, Keyword, V1, V2);
  482.  
  483.       if V1 = 0 then
  484.          return Default;
  485.  
  486.       else
  487.          V := 0;
  488.  
  489.          for J in V1 .. V2 loop
  490.             if Form (J) not in '0' .. '9' then
  491.                raise Use_Error;
  492.             else
  493.                V := V * 10 + Character'Pos (Form (J)) - Character'Pos ('0');
  494.             end if;
  495.  
  496.             if V > 999_999 then
  497.                raise Use_Error;
  498.             end if;
  499.          end loop;
  500.  
  501.          return V;
  502.       end if;
  503.    end Form_Integer;
  504.  
  505.    --------------------
  506.    -- Form_Parameter --
  507.    --------------------
  508.  
  509.    procedure Form_Parameter
  510.      (Form    : String;
  511.       Keyword : String;
  512.       Start   : out Natural;
  513.       Stop    : out Natural)
  514.   is
  515.  
  516.       Klen : constant Integer := Keyword'Length;
  517.  
  518.    --  Start of processing for Form_Parameter
  519.  
  520.    begin
  521.       for J in Form'First + Klen .. Form'Last - 1 loop
  522.          if Form (J) = '='
  523.            and then Form (J - Klen .. J - 1) = Keyword
  524.          then
  525.             Start := J + 1;
  526.             Stop := Start - 1;
  527.  
  528.             while Form (Stop + 1) /= Ascii.NUL
  529.               and then Form (Stop + 1) /= ','
  530.             loop
  531.                Stop := Stop + 1;
  532.             end loop;
  533.  
  534.             return;
  535.          end if;
  536.       end loop;
  537.  
  538.       Start := 0;
  539.    end Form_Parameter;
  540.  
  541.    -------------
  542.    -- Is_Open --
  543.    -------------
  544.  
  545.    function Is_Open (File : in AFCB_Ptr) return Boolean is
  546.    begin
  547.       return (File /= null);
  548.    end Is_Open;
  549.  
  550.    ----------
  551.    -- Mode --
  552.    ----------
  553.  
  554.    function Mode (File : in AFCB_Ptr) return File_Mode is
  555.    begin
  556.       if File = null then
  557.          raise Status_Error;
  558.       else
  559.          return File.Mode;
  560.       end if;
  561.    end Mode;
  562.  
  563.    ----------
  564.    -- Name --
  565.    ----------
  566.  
  567.    function Name (File : in AFCB_Ptr) return String is
  568.    begin
  569.       if File = null then
  570.          raise Status_Error;
  571.       else
  572.          return File.Name.all (1 .. File.Name'Length - 1);
  573.       end if;
  574.    end Name;
  575.  
  576.    ----------
  577.    -- Open --
  578.    ----------
  579.  
  580.    procedure Open
  581.      (File_Ptr  : in out AFCB_Ptr;
  582.       Dummy_FCB : in out AFCB'Class;
  583.       Mode      : File_Mode;
  584.       Name      : String;
  585.       Form      : String;
  586.       Amethod   : Character;
  587.       Creat     : Boolean;
  588.       Text      : Boolean;
  589.       C_Stream  : FILEs := NULL_Stream)
  590.    is
  591.       Stream : FILEs := C_Stream;
  592.       --  Stream which we open in response to this request
  593.  
  594.       Shared : Shared_Status_Type;
  595.       --  Setting of Shared_Status field for file
  596.  
  597.       Fopstr : aliased Fopen_String;
  598.       --  Mode string used in fopen call
  599.  
  600.       Fmoder : aliased constant String (1 .. 2) := "r" & Ascii.NUL;
  601.       --  Used for test open to see if file exists
  602.  
  603.       Formstr : aliased String (1 .. Form'Length + 1);
  604.       --  Form string with Ascii.NUL appended, folded to lower case
  605.  
  606.       Tempfile : constant Boolean := (Name'Length = 0);
  607.       --  Indicates temporary file case
  608.  
  609.       Namelen : constant Integer := Integer'Max (Temp_Len, Name'Length);
  610.       --  Length required for file name, not including final Ascii.NUL
  611.  
  612.       Namestr : aliased String (1 .. Namelen + 1);
  613.       --  Name as given or temporary file name with Ascii.NUL appended
  614.  
  615.       Fullname : aliased String (1 .. max_path_len + 1);
  616.       --  Full name (as required for Name function, and as stored in the
  617.       --  control block in the Name field) with Ascii.NUL appended.
  618.  
  619.       Full_Name_Len : Integer;
  620.       --  Length of name actually stored in Fullname
  621.  
  622.    begin
  623.       if File_Ptr /= null then
  624.          raise Status_Error;
  625.       end if;
  626.  
  627.       --  Acquire form string, setting required NUL terminator
  628.  
  629.       Formstr (1 .. Form'Length) := Form;
  630.       Formstr (Formstr'Last) := Ascii.NUL;
  631.  
  632.       --  Convert form string to lower case
  633.  
  634.       for J in Formstr'Range loop
  635.          if Formstr (J) in 'A' .. 'Z' then
  636.             Formstr (J) := Character'Val (Character'Pos (Formstr (J)) + 32);
  637.          end if;
  638.       end loop;
  639.  
  640.       --  Acquire setting of shared parameter
  641.  
  642.       declare
  643.          V1, V2 : Natural;
  644.  
  645.       begin
  646.          Form_Parameter (Formstr, "shared", V1, V2);
  647.  
  648.          if V1 = 0 then
  649.             Shared := None;
  650.  
  651.          elsif Formstr (V1 .. V2) = "yes" then
  652.             Shared := Yes;
  653.  
  654.          elsif Formstr (V1 .. V2) = "no" then
  655.             Shared := No;
  656.  
  657.          else
  658.             raise Use_Error;
  659.          end if;
  660.       end;
  661.  
  662.       --  Remaining processing is done with tasking locked out. This ensures
  663.       --  that the global data structures (temporary file chain and the open
  664.       --  file chain) retain their integrity.
  665.  
  666.       Lock_Task;
  667.  
  668.       --  If we were given a stream (call from xxx.C_Streams.Open), then set
  669.       --  full name to null and that is all we have to do in this case so
  670.       --  skip to end of processing.
  671.  
  672.       if Stream /= NULL_Stream then
  673.          Fullname (1) := Ascii.Nul;
  674.          Full_Name_Len := 1;
  675.  
  676.       --  Normal case of Open or Create
  677.  
  678.       else
  679.          --  If temporary file case, get temporary file name and add
  680.          --  to the list of temporary files to be deleted on exit.
  681.  
  682.          if Tempfile then
  683.             if not Creat then
  684.                Unlock_Task;
  685.                raise Name_Error;
  686.             end if;
  687.  
  688.             Namestr := Temp_Base & "XXXXXX" & Ascii.NUL;
  689.             mktemp (Namestr'Address);
  690.  
  691.             if Namestr (1) = Ascii.NUL then
  692.                Unlock_Task;
  693.                raise Use_Error;
  694.             end if;
  695.  
  696.             Temp_Files :=
  697.               new Temp_File_Record'(Name => Namestr, Next => Temp_Files);
  698.  
  699.          --  Normal case of non-null name given
  700.  
  701.          else
  702.             Namestr (1 .. Name'Length) := Name;
  703.             Namestr (Name'Length + 1)  := Ascii.NUL;
  704.          end if;
  705.  
  706.          --  Get full name in accordance with the advice of RM A.8.2(22).
  707.  
  708.          full_name (Namestr'Address, Fullname'Address);
  709.  
  710.          for J in Fullname'Range loop
  711.             if Fullname (J) = Ascii.NUL then
  712.                Full_Name_Len := J;
  713.                exit;
  714.             end if;
  715.          end loop;
  716.  
  717.          --  If Shared=None or Shared=Yes, then check for the existence
  718.          --  of another file with exactly the same full name.
  719.  
  720.          if Shared /= No then
  721.             declare
  722.                P : AFCB_Ptr;
  723.  
  724.             begin
  725.                P := Open_Files;
  726.                while P /= null loop
  727.                   if Fullname (1 .. Full_Name_Len) = P.Name.all then
  728.  
  729.                      --  If we get a match, and either file has Shared=None,
  730.                      --  then raise Use_Error, since we don't allow two files
  731.                      --  of the same name to be opened unless they specify the
  732.                      --  required sharing mode.
  733.  
  734.                      if Shared = None
  735.                        or else P.Shared_Status = None
  736.                      then
  737.                         Unlock_Task;
  738.                         raise Use_Error;
  739.  
  740.                      --  If both files have Shared=Yes, then we acquire the
  741.                      --  stream from the located file to use as our stream.
  742.  
  743.                      elsif Shared = Yes
  744.                        and then P.Shared_Status = Yes
  745.                      then
  746.                         Stream := P.Stream;
  747.                         exit;
  748.  
  749.                      --  Otherwise one of the files has Shared=Yes and one
  750.                      --  has Shared=No. If the current file has Shared=No
  751.                      --  then all is well but we don't want to share any
  752.                      --  other file's stream. If the current file has
  753.                      --  Shared=Yes, we would like to share a stream, but
  754.                      --  not from a file that has Shared=No, so in either
  755.                      --  case we just keep going on the search.
  756.  
  757.                      else
  758.                         null;
  759.                      end if;
  760.                   end if;
  761.  
  762.                   P := P.Next;
  763.                end loop;
  764.             end;
  765.          end if;
  766.  
  767.          --  Open specified file if we did not find an existing stream
  768.  
  769.          if Stream = NULL_Stream then
  770.             Fopen_Mode (Mode, Text, Creat, Amethod, Fopstr);
  771.  
  772.             --  A special case, if we are opening (OPEN case) a file and
  773.             --  the mode returned by Fopen_Mode is not "r" or "r+", then
  774.             --  we first do an open in "r" mode to make sure that the file
  775.             --  exists as required by Ada semantics, we then reopen in the
  776.             --  required mode.
  777.  
  778.             if Creat = False and then Fopstr (1) /= 'r' then
  779.                Stream := fopen (Namestr'Address, Fmoder'Address);
  780.  
  781.                if Stream = NULL_Stream then
  782.                   Unlock_Task;
  783.                   raise Name_Error;
  784.                else
  785.                   Stream := freopen (Namestr'Address, Fopstr'Address, Stream);
  786.                end if;
  787.  
  788.             --  Normal case, we can open the file directly with the given mode
  789.  
  790.             else
  791.                Stream := fopen (Namestr'Address, Fopstr'Address);
  792.             end if;
  793.  
  794.             if Stream = NULL_Stream then
  795.                Unlock_Task;
  796.                raise Name_Error;
  797.             end if;
  798.          end if;
  799.       end if;
  800.  
  801.       --  Stream has been successfully located or opened, so now we are
  802.       --  committed to completing the opening of the file. Allocate block
  803.       --  on heap and fill in its fields.
  804.  
  805.       File_Ptr := AFCB_Allocate (Dummy_FCB);
  806.  
  807.       File_Ptr.Is_Regular_File   := (is_regular_file (fileno (Stream)) /= 0);
  808.       File_Ptr.Is_System_File    := False;
  809.       File_Ptr.Is_Text_File      := Text;
  810.       File_Ptr.Shared_Status     := Shared;
  811.       File_Ptr.Access_Method     := Amethod;
  812.       File_Ptr.Stream            := Stream;
  813.       File_Ptr.Form              := new String'(Formstr);
  814.       File_Ptr.Name              := new String'(Fullname (1 .. Full_Name_Len));
  815.       File_Ptr.Mode              := Mode;
  816.       File_Ptr.Is_Temporary_File := False;
  817.  
  818.       Chain_File (File_Ptr);
  819.       Unlock_Task;
  820.       Append_Set (File_Ptr);
  821.    end Open;
  822.  
  823.    --------------
  824.    -- Read_Buf --
  825.    --------------
  826.  
  827.    procedure Read_Buf (File : AFCB_Ptr; Buf : Address; Siz : size_t) is
  828.       Nread : size_t;
  829.  
  830.    begin
  831.       Nread := fread (Buf, 1, Siz, File.Stream);
  832.  
  833.       if Nread = Siz then
  834.          return;
  835.  
  836.       elsif ferror (File.Stream) /= 0 then
  837.          raise Device_Error;
  838.  
  839.       elsif Nread = 0 then
  840.          raise End_Error;
  841.  
  842.       else -- 0 < Nread < Siz
  843.          raise Data_Error;
  844.       end if;
  845.  
  846.    end Read_Buf;
  847.  
  848.    procedure Read_Buf
  849.      (File  : AFCB_Ptr;
  850.       Buf   : Address;
  851.       Siz   : in Interfaces.C_Streams.size_t;
  852.       Count : out Interfaces.C_Streams.size_t)
  853.    is
  854.    begin
  855.       Count := fread (Buf, 1, Siz, File.Stream);
  856.  
  857.       if Count = 0 and then ferror (File.Stream) /= 0 then
  858.          raise Device_Error;
  859.       end if;
  860.    end Read_Buf;
  861.  
  862.    -----------
  863.    -- Reset --
  864.    -----------
  865.  
  866.    --  The reset which does not change the mode simply does a rewind.
  867.  
  868.    procedure Reset (File : in out AFCB_Ptr) is
  869.    begin
  870.       Check_File_Open (File);
  871.       rewind (File.Stream);
  872.    end Reset;
  873.  
  874.    --  The reset with a change in mode is done using freopen, and is
  875.    --  not permitted except for regular files (since otherwise there
  876.    --  is no name for the freopen, and in any case it seems meaningless)
  877.  
  878.    procedure Reset (File : in out AFCB_Ptr; Mode : in File_Mode) is
  879.       Fopstr : aliased Fopen_String;
  880.  
  881.    begin
  882.       Check_File_Open (File);
  883.  
  884.       --  If mode is not really changing, then we simply rewind the stream
  885.       --  this is permitted in all cases except for non-regular files, where
  886.       --  rewind can't work.
  887.  
  888.       if Mode = File.Mode and then File.Is_Regular_File then
  889.  
  890.          if Mode /= Append_File then
  891.             rewind (File.Stream);
  892.          end if;
  893.  
  894.       --  Change of mode not allowed for shared file or file with no name
  895.       --  or file that is not a regular file, or for a system file.
  896.  
  897.       elsif File.Shared_Status = Yes
  898.         or else File.Name'Length <= 1
  899.         or else File.Is_System_File
  900.         or else (not File.Is_Regular_File)
  901.       then
  902.          raise Use_Error;
  903.  
  904.       --  Here the change of mode is permitted, we do it by reopening the
  905.       --  file in the new mode and replacing the stream with a new stream.
  906.  
  907.       else
  908.          Fopen_Mode
  909.            (Mode, File.Is_Text_File, False, File.Access_Method, Fopstr);
  910.  
  911.          File.Stream :=
  912.            freopen (File.Name.all'Address, Fopstr'Address, File.Stream);
  913.  
  914.          if File.Stream = NULL_Stream then
  915.             Close (File);
  916.             raise Use_Error;
  917.  
  918.          else
  919.             File.Mode := Mode;
  920.             Append_Set (File);
  921.          end if;
  922.       end if;
  923.    end Reset;
  924.  
  925.    ---------------
  926.    -- Write_Buf --
  927.    ---------------
  928.  
  929.    procedure Write_Buf (File : AFCB_Ptr; Buf : Address; Siz : size_t) is
  930.    begin
  931.       if fwrite (Buf, 1, Siz, File.Stream) /= Siz then
  932.          raise Device_Error;
  933.       end if;
  934.    end Write_Buf;
  935.  
  936. end System.File_IO;
  937.